import sys

from PyQt5.QtCore import Qt, pyqtSignal, QTimer, QRectF, QRect
from PyQt5.QtGui import QFont, QColor, QPainter, QPainterPath, QRadialGradient
from PyQt5.QtWidgets import QApplication, QWidget


class SwitchBtn(QWidget):
    checkedChanged = pyqtSignal(bool)

    def __init__(self, parent=None):
        super().__init__(parent)
        # 开关状态，默认关闭
        self.checked = False
        # 可用状态，默认可用
        self.enabled = True
        # 颜色
        self.sliderColorOff = None
        self.sliderColorOn = None
        self.edge = QColor(196, 51, 54)  # 边框颜色
        self.bgColorOff = self.bgColorOn = QColor(255, 255, 255)  # 滑动条颜色
        self.textColorOff = self.textColorOn = QColor(85, 85, 85)  # 文本颜色
        # 初始文本
        self.textOff = ""
        self.textOn = ""
        # 控件边距
        self.space = 2
        # 每次移动的步长为宽度的50分之一
        self.step = self.width() / 50
        # 起始、终点
        self.startX = 0
        self.endX = 0
        # 滑块移动定时器
        self.timer = QTimer()
        self.timer.setInterval(5)
        self.timer.timeout.connect(self.update_slider)
        # 控件可用状态查询
        self.enabled_timer = QTimer()
        self.enabled_timer.setInterval(5)
        self.enabled_timer.timeout.connect(self.update_color)
        self.enabled_timer.start()
        # 字体
        font = QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(9)
        self.setFont(font)

    def set_color(self):
        """滑块形状颜色"""
        # 关闭状态 滑块形状、颜色
        self.sliderColorOff = QRadialGradient(
            self.width() / 2,
            self.height() / 2,
            self.width() / 2,
            self.width() / 6,
            self.height() / 6,
        )
        self.sliderColorOff.setColorAt(0, QColor(232, 251, 255))
        self.sliderColorOff.setColorAt(0.8, QColor(71, 69, 69))
        # 开启状态 滑块形状、颜色
        self.sliderColorOn = QRadialGradient(
            self.width() / 2,
            self.height() / 2,
            self.width() / 2,
            3 * self.width() / 4,
            self.height() / 2,
        )
        if self.isEnabled():  # 控件可用
            self.edge = QColor(196, 51, 54)  # 边框颜色/红色
            self.sliderColorOn.setColorAt(0, QColor(232, 251, 255))  # 滑块开启时为绿色
            self.sliderColorOn.setColorAt(0.8, QColor(20, 200, 20))
        else:  # 控件不可用
            self.edge = QColor(141, 141, 141)  # 边框颜色/灰色
            self.sliderColorOn.setColorAt(0, QColor(232, 251, 255))  # 滑块开启时为暗绿色
            self.sliderColorOn.setColorAt(0.8, QColor(71, 140, 69))

    def update_color(self):
        """刷新控件颜色"""
        self.set_color()
        if self.enabled != self.isEnabled():
            self.enabled = self.isEnabled()
            self.update()

    def resizeEvent(self, event):
        """形状变动时，滑块跟随变动，移动速度变动"""
        self.set_color()
        self.step = self.width() / 50
        self.endX = self.width() - self.height()
        if self.checked:
            self.startX = self.endX
        else:
            self.startX = 0
        self.update()

    def isChecked(self):
        return self.checked

    def setChecked(self, state):
        """设置状态"""
        if self.checked != state:
            self.checked = state
            if self.checked:
                self.endX = self.width() - self.height()
            else:
                self.endX = 0
            self.timer.start()

    def setOffText(self, text):
        self.textOff = text

    def setOnText(self, text):
        self.textOn = text

    def update_slider(self):
        if self.checked:
            if self.startX < self.endX:
                self.startX = self.startX + self.step
            else:
                self.startX = self.endX
                self.timer.stop()
        else:
            if self.startX > self.endX:
                self.startX = self.startX - self.step
            else:
                self.startX = self.endX
                self.timer.stop()
        self.update()

    def mousePressEvent(self, event):
        self.checked = not self.checked
        # 发射信号
        self.checkedChanged.emit(self.checked)
        # 状态切换改变后自动计算终点坐标
        if self.checked:
            self.endX = self.width() - self.height()
        else:
            self.endX = 0
        self.timer.start()

    def paintEvent(self, evt):
        # 绘制准备工作, 启用反锯齿
        painter = QPainter()
        painter.begin(self)
        painter.setRenderHint(QPainter.Antialiasing)
        # 绘制背景
        self.draw_back(painter)
        # 绘制滑块
        self.draw_slider(painter)
        # 绘制文字
        self.draw_text(painter)
        painter.end()

    def draw_text(self, painter):
        painter.save()
        if self.checked:
            painter.setPen(self.textColorOn)
            rect = QRect(0, 0, self.width() / 2 + self.space * 2, self.height())
            # rect = QRect(self.space * 4, 0, self.width() / 2 + self.space * 2, self.height())
            painter.drawText(rect, Qt.AlignCenter, self.textOn)
        else:
            painter.setPen(self.textColorOff)
            rect = QRect(
                self.width() / 2, 0, self.width() / 2 - self.space, self.height()
            )
            # rect = QRect(self.width() / 2, 0, self.width() / 2 - self.space, self.height())
            painter.drawText(rect, Qt.AlignCenter, self.textOff)
        painter.restore()

    def draw_back(self, painter):
        painter.save()
        painter.setPen(self.edge)
        if self.checked:
            painter.setBrush(self.bgColorOn)
        else:
            painter.setBrush(self.bgColorOff)
        rect = QRect(0, 0, self.width(), self.height())
        # 半径为高度的一半
        radius = rect.height() / 2
        # 圆的宽度为高度
        circle_width = rect.height()
        path = QPainterPath()
        path.moveTo(radius, rect.left())
        path.arcTo(QRectF(rect.left(), rect.top(), circle_width, circle_width), 90, 180)
        path.lineTo(rect.width() - radius, rect.height())
        path.arcTo(
            QRectF(
                rect.width() - rect.height(), rect.top(), circle_width, circle_width
            ),
            270,
            180,
        )
        path.lineTo(radius, rect.top())
        painter.drawPath(path)
        painter.restore()

    def draw_slider(self, painter):
        painter.save()
        if self.checked:
            painter.setBrush(self.sliderColorOn)
        else:
            painter.setBrush(self.sliderColorOff)
        rect = QRect(0, 0, self.width(), self.height())
        slider_width = rect.height() - self.space * 4
        slider_rect = QRect(
            self.startX + self.space * 2, self.space * 2, slider_width, slider_width
        )
        painter.drawEllipse(slider_rect)
        painter.restore()
